Sound Sampling on an Apple ][,][+,//e
by John MacLean
Copyright (c) 1990 Apple Users' Group, Sydney
Republished from Applecations, a publication of the Apple Users' Group, Sydney, Australia.
with technical assistance by Richard Bennett.
With the recent introduction of the Apple IIGS there has been much interest in sound sampling and digitising. I was recently surprised by the quality of some digitised sound generated on a //e, so I uncovered my old ][+, borrowed a drive off my GS and went to work.
The sound is sampled through the old cassette port so if
you have kept your cassette leads over the years they may finally get used (again). The sound is played back through the internal speaker, so all you need is a cassette player or a CD for better sound quality. With the routines I will present here, you can achieve about 30 seconds of digitised sound in a 48K machine. The routines are written in assembler, but you can type them and enjoy them without knowing how they work.
The routines are surprisingly simple:
The cassette-in port (location $C060) changes sign (the high bit changes) whenever there is a change in the electrical signal coming from the external sound. If the speaker is clicked every time the external signal changes, the sound is reproduced. This leads to the first program:
LOOP1 LDA $C060 ;Loop until the cassette-in goes
BPL LOOP1 ;negative.
LDA $C030 ;Click the speaker.
BIT $00 ;Waste 3 machine cycles.
LOOP2 LDA $C060 ;Loop until the cassette-in goes
BMI LOOP2 ;positive.
LDA $C030 ;Click the speaker.
JMP LOOP1 ;Keep going
This short routine takes the signal straight from the cassette-in port and reproduces the sound on the internal speaker. Note the BIT $00 instruction to waste 3 machine cycles - all loops should be the same number of machine cycles for the best quality sound reproduction. This routine is useful for adjusting your volume (and equalising if you have the equipment) to produce the best quality sound.
Now what is needed is some way of recording the signal from the cassette-in port so the sound can be replayed later. The next routine does that recording:
********************************
* *
* CASSETTE PORT TO MEMORY PRGM *
* *
* WRITTEN BY JOHN MACLEAN 1987 *
* *
********************************
LST OFF
********************************
ORG $8000
********************************
* ZERO PAGE LOCATIONS
BUFF EQU $06
ZPAGE EQU $08
********************************
* HARDWARE PAGE LOCATIONS
CASSPORT EQU $C060
********************************
* SET UP THE BUFFER
START LDA #$00
STA BUFF
LDA #$08
STA BUFF+1
LDY #$00
* FILL BUFFER WITH ZEROS TO ALLOW FOR TIME OUTS
LDA #$00
ZLOOP STA (BUFF),Y
INY
BNE ZLOOP
INC BUFF+1
BPL ZLOOP
* RESET THE BUFFER
LDA #$08
STA BUFF+1
* LOOP HERE UNTIL CASSPORT BECOMES POSITIVE
MLOOP0 PHA ;3
PLA ;4
MLOOP1 LDX #$01 ;2
MLOOP2 LDA CASSPORT ;4
BPL PSAVE ;2+
BIT ZPAGE ;3
INX ;2
BEQ MSKIP ;2+
JSR MDELAY12 ;12
JMP MLOOP2 ;3
MSAVE TXA ;2
STA (BUFF),Y ;5+
MSKIP INY ;2
BNE MLOOP0 ;2+
INC BUFF+1 ;5
BPL MLOOP1 ;2+
MDELAY12 RTS
* LOOP HERE UNTIL CASSPORT BECOMES NEGATIVE
PLOOP0 PHA ;3
PLA ;4
PLOOP1 LDX #$01 ;2
PLOOP2 LDA CASSPORT ;4
BMI MSAVE ;2+
BIT ZPAGE ;3
INX ;2
BEQ PSKIP ;2+
JSR PDELAY12 ;12
JMP PLOOP2 ;3
PSAVE TXA ;2
STA (BUFF),Y ;5+
PSKIP INY ;2
BNE PLOOP0 ;2+
INC BUFF+1 ;5
BPL PLOOP1 ;2+
PDELAY12 RTS
The time interval between each change in sign of the cassette-in port is recorded sequentially from $0800 to $8000 in main memory. This is done by incrementing the X register each time the port is tested and unchanged. When it finally changes the X register is buffered. The routine consists of two almost identical smaller routines. These routines could be combined into one general routine, at the cost of about 10 machine cycles. The faster the loops, the higher the sampling rate, and thus the sampled sound is of higher quality. The unusual coding ensures that the number of cycles between each sampling of the cassette-in port is the same regardless of the branches taken.
The sound is now recorded (at 28 machine cycle intervals) and must be played back at exactly the same speed. This is achieved by decrementing the buffered values and clicking the speaker once they become zero. In this way, the intervals between speaker clicks will be the same as the intervals between changes of sign of the cassette-in port during recording. The following code plays back the sound as described. It uses unconventional techniques to get the loops down to 28 machine cycles each so the sound will be accurately reproduced.
********************************
* *
* MEMORY TO SPEAKER PROGRAM. *
* *
* WRITTEN BY JOHN MACLEAN 1987 *
* *
********************************
LST OFF
********************************
ORG $8200
********************************
* ZERO PAGE LOCATIONS
BUFF EQU $06
ZPAGE EQU $08
********************************
* HARDWARE PAGE LOCATIONS
SPEAKER EQU $C030
********************************
* SET UP THE BUFFER
START LDA #$00
STA BUFF
LDA #$08
STA BUFF+1
LDY #$00
JMP LOOP1
* READ THE NEXT BYTE FROM THE BUFFER
LOOP0 INC BUFF+1 ;5
BMI DELAY12 ;2+ (ALL DONE ?)
LOOP1 LDA (BUFF),Y ;5+
TAX ;2
DEX ;2
BEQ SKIP1 ;2+
* CHECK FOR ZERO (TIME OUT) AND HANDLE SEPARATELY
* OTHERWISE DELAY 28 CYCLES TIMES THE VALUE IN THE X
* REGISTER
CPX #$FF ;2 (WAS IT A ZERO BYTE ?)
BNE DELAYX ;2+
JSR DELAY12 ;12
JSR DELAY12 ;12
NOP ;2
* DELAY ((254 * 2) + 1) CYCLES
DEX ;2
LOOP3 JSR DELAY12 ;12
BIT ZPAGE ;3
BIT ZPAGE ;3
BIT ZPAGE ;3
NOP ;2
DEX ;2
BNE LOOP3 ;2+
* GO AND INCREMENT THE BUFFER POINTER BUT DO NOT CLICK
* SPEAKER
JMP SKIP2 ;3
* USE UP THE REST OF ANOTHER 28 CYCLES TIMING IT SO WE CAN
* BRANCH TO THE REST OF THE LOOP COMPLETING 56 (2 * 28)
* CYCLES
* ON QUEUE. IF X WAS GREATER THAN 2 THEN DELAY 28 CYCLES FOR
* EACH REMAINING DECREMENT OF THE X REGISTER
DELAYX JSR DELAY12 ;12
BIT ZPAGE ;3
NOP ;2
NOP ;2
DEX ;2
BEQ SKIP1 ;2+
* THIS ROUTINE WILL DELAY 28 CYCLES TIMES THE X REGISTER
* NOTE THE NOP MAKES UP FOR THE PREVIOUS BRANCH NOT BEING
* TAKEN (1 CYCLE) AND THE LAST BRANCH OF THE DELAY LOOP NOT
* BEING TAKEN THE LAST TIME THROUGH THE LOOP (1 CYCLE)
LOOP2 JSR DELAY12 ;12
BIT ZPAGE ;3
BIT ZPAGE ;3
BIT ZPAGE ;3
NOP ;2
DEX ;2
BNE LOOP2 ;2+
NOP ;2
SKIP1 LDA SPEAKER ;4
SKIP2 INY ;2
BEQ LOOP0 ;2+
BIT ZPAGE ;3
NOP ;2
JMP LOOP1 ;3
DELAY12 RTS
Once you have recorded your favourite musics, try modifying the routines. Possible modifications are:
- Slow down the recording program (add some NOP'S) and the sound will play back faster.
- Slow down the play back program and the sound will play back slower.
- Modify both the recording and playback routines to increase the recording time. Use the following buffers so the tests can be BEQ, BMI, and BPL at the end of the buffers:
Main memory : $0800 -> $7FFF,
$BFFF -> $8000,
$D000 -> $FFFF (on the language card),
Aux memory : $0800 -> $7FFF,
$BFFF -> $8000,
$D000 -> $FFFF (alternate language card).
- Write a routine to play the sound in reverse (or just reverse the recorded buffer).
Happy sampling, and there's no need to feel left out if you don't own a GS.

THIS CONTENT COPYRIGHT © 2007, APPLE MACINTOSH USERS' GROUP, SYDNEY
Permission has been obtained to make this material available on the Internet.

Permission is hereby granted for non-profit user groups to republish this content.
PLEASE CREDIT THE AUTHOR AND THE SOURCE: Applecations, publication of the Apple Users' Group, Sydney, Australia

THIS PAGE COPYRIGHT © 2007, ANDREW ROUGHAN